Purpose. In this work, we will explore the relation between identified measures of despair of interest (e.g., personality measures of self-consciousness, individual and composite item scores from the CES-D assessment) and descriptors of diseases of despair. We will achieve this goal through modeling the outcomes based on the included predictors, and robustly assess the importance of the included features in predicting the outcomes via bootstrapping. We will use two well-known machine learning models, random forests and LASSO, which are both frequently used to measure the relative importance of the predictors included in the models. Lastly, we’ll generate trained and tuned models using this reduced feature set which can be used by others wish to predict the identified outcomes.
Subject inclusion. For this investigation, we will omit the entirety of Wave 2. This is commonly done in analyses of AddHealth data due the design of the original study. Otherwise, our dataset will include only subjects who have predictor and outcome data in all of the waves.
Outcome variables. In this experiment, we assess suicidal ideation at Wave 5.
Predictor variables. The predictors for these models are hand-picked, and based on previous work, relevance, and subject matter expertise. The set of predictors and the set of outcomes are disjoint. Predictors from Waves 1-4 (excluding Wave 2, see above) are included, and will be detailed in the following analysis.
seed= 3895
set.seed(seed)
Pipeline overview

Dataset generation
The predictors we will be using will be the the variable predictor_list loaded from 10-import-data.Rmd file. These initial set of predictors will be based of the list of variables that describe anxiety, depression, and optimism.
## set outcome variable of interest
outcome = 'h5mn8'
wave_data <- load_waves(1:5)
full_dataset <- get_working_dataset_full(wave_data, join_type = 'full')
## Only study the subjects that we're interested in.
inner_aids <- get_inner(list(wave_data[[1]], wave_data[[3]], wave_data[[4]], wave_data[[5]]))
## get na_levels : dataset to recode all skip levels in variables
na_levels <- read_csv("na_levels.csv")
## use the features and ids that you want to select out what you want
suicide_ds <- full_dataset %>%
filter(aid %in% inner_aids) %>%
remove_subjects_not_in_wave1() %>%
add_demographics() %>%
add_bio_despair() %>%
dplyr::select(aid, outcome, all_of(c(predictor_list, demographic_age_list, demographic_list))) %>%
dplyr::select(-c(h5waist,h5bmi,h5dbp,h5bpjcls,h5bpcls4,h5sbp)) %>%
recode_missing_levels(na_levels)
[1] "Recoding Missing Factor Variables"
[1] "Factor variables being recoded : 63"
[1] "Recoding Missing Numeric Variables"
[1] "Numeric variables being recoded : 6"
Outcome variable: Binarizing and recoding details
The following table details the values present in the outcome variable. As we can see, we need to convert the variable into a factor, and drop the NAs.
suicide_ds %>%
group_by(get(outcome)) %>%
summarise(total = n(), type = class(get(outcome)))
The table also demonstrates that the classes are very imbalanced, with about 14x the negative class as compared with the positive class.
suicide_ds <- suicide_ds %>%
mutate_at(vars(outcome), as.factor) %>%
drop_na(outcome)
After dropping the NAs, we see that we now have 9168 rows, which is consistent with the table above. The following plot visually displays the class distribution.
suicide_ds %>% explore_outcome(outcome)

The distribution of the data is a distinct imbalance as noted above. I think this warrants using pr_auc as the optimization and selection metric.
Data exploration and visualization
Here, we comment about the general characteristics of the data based on the provided visualizations. We comment on missingness of data, any strange or unusual behavior (e.g., strong imbalances), and any correlation that sticks out.
#Report about the characteristics of the subjects left out of the join
suicide_ds %>% explore_dropped()

# Visualize distributions of variables of interest
suicide_ds %>%
dplyr::select(-aid) %>%
graph_bar_discrete(df = .,
plot_title = "Distributions of Discrete Variables",
max_categories = 50,
num_rows = 3,
num_cols = 3,
x_axis_size = 12,
y_axis_size = 12,
title_size = 15)








suicide_ds %>%
graph_missing(only_missing = TRUE,
title = "Percent Missing",
box_line_size = .5,
label_size = .5,
x_axis_size = 12,
y_axis_size = 12,
title_size = 15)

suicide_ds %>%
#dplyr::select(1:20) %>%
pairwise_cramers_v() %>%
plot_cramer_v(x_axis_angle = 90,
plot_title = "Association among Categorical Variables",
interactive = TRUE)
The correlation plot suggests that there are several variables we might think about removing. Firstly, there are moderate correlations among the individual predictor blocks; this is due to the way that they are ordered, since they’re essentially grouped into subsets. Addtionally, some predictor pairs have extremely high correlations, like (h4id5j, h4mh26) (correlation of 0.72), and many variables in the h4mh* series. (h3id5j, h4id5h) also has high correlation > 0.5. We may want to consider removing several of these in addition to the age variables because it may cause feature importance masking within our approaches.
Machine learning split of the data
In this section, we split the data to ensure that our model is able to generalize to other datasets.
## split the data into relevant proportions desired
data_splits <- suicide_ds %>%
split_data(strat_var = outcome, ratios=c(0.7, 0.2, 0.1))
# assemble list
training_df <- data_splits$train
validation_df <- data_splits$valid
testing_df <- data_splits$test
Robust feature evaluation
RF model
The RF models are chosen based on a grid search using the following the parameters:
- max depth: maximum depth allowed for a single tree in the RF
- number of trees: maximum number of trees allowed in the RF
- mtries: the number of columns sampled for each tree split
- min_rows: the minimum number of rows required to split the internal node
- balance classes: whether to balance the classes or not
- stopping_metric: metric which results in early stopping of training of the model
- categorical encoding: use one hot encoding to create straightforward comparison with LASSO
The following table displays the mean performance metrics for the bootstrapped models on the validation set, removing values for which there are NA.
mean_bs_rf_perf <- get_metric_set_from_perfs(suicide_rf$perfs) %>%
dplyr::select(accuracy, mpce, sens, spec, ppv, npv, roc_auc, pr_auc,
tns, tps, fns, fps, no_n, no_p, err_rate, bal_accuracy, everything()) %>%
summarise_if(is.numeric, mean, na.rm=TRUE) %>%
mutate(model = 'bs_rf') %>%
dplyr::select(model, everything())
mean_bs_rf_perf
As shown, the bootstrapped models tend to have high specificity but low sensitivity, indicating that there is a challenge in identifying subjects with suicidal ideation.
Feature importances: Random Forest
Mean decrease in impurity (MDI)
boot_rf_mdi <- suicide_rf$mdi %>%
get_median_placement(use_base_var = TRUE) %>%
add_attribute_names('predictor', full_dataset) %>%
dplyr::select(predictor, att_name, overall_rank)
head(boot_rf_mdi, 20)
This table returns the MDI variable importance ranks that returned from each of the bootstrapped models.
# Needs to be fixed so that axes don't overlap each other and obscure understanding
plot_placement_boxplot(suicide_rf$mdi)

Permutation importance
Now, let’s look at the permutation importance:
boot_rf_perm_plt <- suicide_rf$models %>%
get_aggregated_permute_imp(training_df, outcome=outcome)
met <- 'pr_auc'
boot_rf_perm <- boot_rf_perm_plt %>%
get_permute_placement(metric_oi=met) %>%
add_attribute_names('predictor', full_dataset) %>%
dplyr::select(predictor, everything())
head(boot_rf_perm, 20)
MDI vs Permutation importance
In this step, we assess the differences generated between the different types of importances.
cbind(boot_rf_mdi[1:20,], dplyr::select(boot_rf_perm[1:20,], -all_of(met)))
As shown, the MDI importance suffers from imbalances due to the number of values associated with a predictor. Because the wave ages have so many more values than the other factors, this artificially inflates their importance in MDI. The permutation importance is more intuitive.
plot_permute_var_imp(boot_rf_perm, metric = pr_auc)

LASSO model
In this step, we model the relation between the outcomes and the predictors using a linear regression with L2 regularization. This drives the importance of unimportant and redudant features towards zero.
# Function parameters
lasso_params <- list(alpha = c(1))
# Call modeling function using function parameters and show visualization of results. Recommend the number of features that should be used. Report performance metric stats.
suicide_lasso <- model_feature_selection( "Lasso",
training_frame = training_df,
validation_frame = validation_df,
hyper_params = lasso_params,
outcome = outcome,
n = n_boot)
mean_bs_lasso_perf <- get_metric_set_from_perfs(suicide_lasso$perfs) %>%
dplyr::select(accuracy, mpce, sens, spec, ppv, npv, roc_auc, pr_auc,
tns, tps, fns, fps, no_n, no_p, err_rate, bal_accuracy, everything()) %>%
summarise_if(is.numeric, mean, na.rm=TRUE) %>%
mutate(model='bs_lasso') %>%
dplyr::select(model, everything())
mean_bs_lasso_perf
Feature importances: LASSO
Coefficient-based variable importance
boot_lasso_mdi <- suicide_lasso$mdi %>%
get_median_placement(use_base_var = TRUE) %>%
add_attribute_names('predictor', full_dataset) %>%
dplyr::select(predictor, att_name, overall_rank)
head(boot_lasso_mdi, 20)
plot_placement_boxplot(suicide_lasso$mdi)

Permutation importance
boot_lasso_perm_plt <- suicide_lasso$models %>%
get_aggregated_permute_imp(training_df, outcome=outcome)
boot_lasso_perm <- boot_lasso_perm_plt %>%
get_permute_placement(metric_oi=met) %>% #set in random forest section
add_attribute_names('predictor', full_dataset) %>%
dplyr::select(predictor, everything())
head(boot_lasso_perm, 20)
plot_permute_var_imp(boot_lasso_perm, metric = pr_auc)

Coefficient vs. Permutation importance
Now, we compare the feature importances generated by the two different approaches. The traditional method of evaluating feature importance for regression methods is through analysis of the coefficients.
cbind(boot_lasso_mdi[1:20,], dplyr::select(boot_lasso_perm[1:20,], -met))
Comparison: Model Type Feature Importance
Here, we look at the aggregated results of the bootstrapped predictors and compare the models generated to each other.
joined_results <- boot_rf_perm %>%
dplyr::select(-met) %>%
full_join(dplyr::select(boot_lasso_perm, -met), by=c("predictor", "att_name"), suffix=c('.rf', '.lasso')) %>%
mutate(mean_rank = (overall_rank.rf+overall_rank.lasso)/2) %>%
arrange(mean_rank)
head(joined_results, 20)
The following visualization provides the intuition about the differences in the rankings between model types. They’re ordered by the overall mean importance, and for a given variable, the differences in rank are shown.
# Comparison of top_n features
joined_results %>%
compare_feature_select(interactive = TRUE,
top_n = 100,
opacity = 0.50,
plot_title = "Permutation Importance of Predictors by Model")
Generation of final model
RF model
In this step, we build the final model for the random forest. We use slightly more values in order to come up with the best model, keeping in mind the number of combinations that are required to run to evaluate the grid.
# # Spans of hyper parameters for random forest
rf_params <- list(max_depth = 50,
ntrees = 150,
mtries = seq(-1, 30, by=5),
min_rows = seq(5, 60, by=5),
balance_classes = c(TRUE, FALSE),
stopping_metric = 'AUCPR',
categorical_encoding = 'one_hot_explicit')
# rf_params <- list(max_depth = seq(20, 50, 20),
# balance_classes = TRUE,
# categorical_encoding= 'one_hot_explicit')
# Function parameters
final_model_rf <- rf_model(outcome,
training_frame = training_df,
validation_frame = validation_df,
nfolds = 5,
hyper_params = rf_params, model_seed=seed)
Features: permutation importance
final_rf_perm_plt <- c(final_model_rf[[1]]) %>%
get_aggregated_permute_imp(training_df, outcome=outcome)
final_rf_perm <- final_rf_perm_plt %>%
get_permute_placement(metric_oi=met) %>%
add_attribute_names('predictor', full_dataset) %>%
dplyr::select(predictor, everything())
head(final_rf_perm, 20)
plot_permute_var_imp(final_rf_perm, metric = pr_auc)

Comparison with bootstrap results
This section investigates the differences in the bootstrap results vs the features generated from the random forest final model. The following table shows the overall differences in rank.
rf_joined_results <- final_rf_perm %>%
dplyr::select(-met) %>%
full_join(dplyr::select(boot_rf_perm, -met), by=c("predictor", "att_name"), suffix=c('.final', '.bootstrap')) %>%
mutate(mean_rank = (overall_rank.final + overall_rank.bootstrap)/2) %>%
arrange(mean_rank)
head(rf_joined_results, 20)
The following plot provides visualizations for the difference in the final model rankings vs the bootstrap.
# Comparison of top_n features
rf_joined_results %>%
compare_feature_select(sel_cols = c("overall_rank.final", "overall_rank.bootstrap"),
interactive = TRUE,
top_n = 100,
opacity = 0.50,
plot_title = "Permutation Importance of Predictors: Final vs. Bootstrap")
LASSO model
Now, we create the final model for LASSO. There is no substantial difference between this method and the bootstrap methods, other than the data upon which the model is being built.
# Function parameters
lasso_params <- list(alpha = c(1))
final_model_lasso <- lasso_model(training_frame = training_df,
validation_frame = validation_df,
outcome = outcome,
nfolds = 5,
hyper_params = lasso_params)
The final LASSO performance metrics are shown below:
# show model final performance
print(final_model_lasso[[2]])
Features: permutation importance
final_lasso_perm_plt <- c(final_model_lasso[[1]]) %>%
get_aggregated_permute_imp(training_df, outcome=outcome)
final_lasso_perm <- final_lasso_perm_plt %>%
get_permute_placement(metric_oi=met) %>%
add_attribute_names('predictor', full_dataset) %>%
dplyr::select(predictor, everything())
head(final_lasso_perm, 20)
plot_permute_var_imp(final_lasso_perm, metric = pr_auc)

Comparison with bootstrap results
This section investigates the differences in the bootstrap results vs the features generated from the LASSO final model. The following table shows the overall differences in rank.
lasso_joined_results <- final_lasso_perm %>%
dplyr::select(-met) %>%
full_join(dplyr::select(boot_lasso_perm, -met), by=c("predictor", "att_name"), suffix=c('.final', '.bootstrap')) %>%
mutate(mean_rank = (overall_rank.final + overall_rank.bootstrap)/2) %>%
arrange(mean_rank)
head(lasso_joined_results, 20)
The following plot provides visualizations for the difference in the final model rankings vs the bootstrap.
# Comparison of top_n features
lasso_joined_results %>%
compare_feature_select(sel_cols = c("overall_rank.final", "overall_rank.bootstrap"),
interactive = TRUE,
top_n = 100,
opacity = 0.50,
plot_title = "Permutation Importance of Predictors: Final vs. Bootstrap")
Comparison: Final model features
Here, we compare the features generated by the permutation importance between the two final models.
rf_lasso_final_joined_results <- final_rf_perm %>%
dplyr::select(-met) %>%
full_join(dplyr::select(final_lasso_perm, -met), by=c("predictor", "att_name"), suffix=c('.rf', '.lasso')) %>%
mutate(mean_rank = (overall_rank.rf+overall_rank.lasso)/2) %>%
arrange(mean_rank)
head(rf_lasso_final_joined_results, 20)
The following visualization provides the intuition about the differences in the rankings between the final model types. They’re ordered by the overall mean importance, and for a given variable, the differences in rank are shown.
# Comparison of top_n features
rf_lasso_final_joined_results %>%
compare_feature_select(sel_cols = c("overall_rank.rf", "overall_rank.lasso"),
interactive = TRUE,
top_n = 100,
opacity = 0.50,
plot_title = "Permutation Importance of Predictors: Random Forest vs Lasso")
Comparison: Final model performance
With the final models generated, we’re now able to compare their performance metrics.
# Comparison of performance metrics
valid_perf <- get_metric_set_from_perfs(perf_list = list(final_model_rf[[2]], final_model_lasso[[2]])) %>%
mutate(model = c('rf', 'lasso'))
testing_perf <- get_metric_set_from_models(testing_df, list(final_model_rf[[1]], final_model_lasso[[1]]), out=outcome) %>%
mutate(model = c('rf', 'lasso'))
Validation and selection. The following table shows the comparison between models in terms of the validation set. We can select our final model based on the best performing model according to the metric.
print(valid_perf)
Testing performance. The following shows the performance of both the models on the test set. Note that although we don’t use this test set to evaluate the final models, we can still see how our selected method would have performed.
print(testing_perf)
The following plots show a comparison between the performance of the models on the validation and test sets. Again, we don’t choose the model based on the test set, but curiosity dictates that we view this performance.
# Show plots side by side
metrics_of_interest = c('model', 'accuracy', 'bal_accuracy', 'mpce', 'sens', 'spec', 'ppv', 'npv', 'pr_auc', 'roc_auc')
valid_plt <- plot_metric_set(dplyr::select(valid_perf, all_of(metrics_of_interest)), plot_title = "Model comparison for validation set")
test_plt <- plot_metric_set(dplyr::select(testing_perf, all_of(metrics_of_interest)), plot_title = "Model comparison for testing set")
gridExtra::grid.arrange(gridExtra::arrangeGrob(valid_plt, test_plt, ncol=2, nrow=1))

Outcome variable discussion
Here, the subject matter experts will comment on the the differences in the features obtained between the studied outcomes variables and discuss the discrepancies and/or cohesion.
# Show differences in features obtained
LS0tCnRpdGxlOiAiNzEtZXhwZXJpbWVudHMtc3VpY2lkYWwiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0aGVtZTogbHVtZW4KICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgdG9jX2RlcHRoOiA0CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgdG9jOiB5ZXMKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UpCmBgYAoKYGBge3Igc291cmNlIGZpbGVzLCBpbmNsdWRlPUZBTFNFfQpzb3VyY2UoImZ1bmN0aW9uX2ltcG9ydC5SIikKYGBgCgoqKlB1cnBvc2UuKiogSW4gdGhpcyB3b3JrLCB3ZSB3aWxsIGV4cGxvcmUgdGhlIHJlbGF0aW9uIGJldHdlZW4gaWRlbnRpZmllZCBtZWFzdXJlcyBvZiBkZXNwYWlyIG9mIGludGVyZXN0IChlLmcuLCBwZXJzb25hbGl0eSBtZWFzdXJlcyBvZiBzZWxmLWNvbnNjaW91c25lc3MsIGluZGl2aWR1YWwgYW5kIGNvbXBvc2l0ZSBpdGVtIHNjb3JlcyBmcm9tIHRoZSBDRVMtRCBhc3Nlc3NtZW50KSBhbmQgZGVzY3JpcHRvcnMgb2YgZGlzZWFzZXMgb2YgZGVzcGFpci4gIFdlIHdpbGwgYWNoaWV2ZSB0aGlzIGdvYWwgdGhyb3VnaCBtb2RlbGluZyB0aGUgb3V0Y29tZXMgYmFzZWQgb24gdGhlIGluY2x1ZGVkIHByZWRpY3RvcnMsIGFuZCByb2J1c3RseSBhc3Nlc3MgdGhlIGltcG9ydGFuY2Ugb2YgdGhlIGluY2x1ZGVkIGZlYXR1cmVzIGluIHByZWRpY3RpbmcgdGhlIG91dGNvbWVzIHZpYSBib290c3RyYXBwaW5nLiAgV2Ugd2lsbCB1c2UgdHdvIHdlbGwta25vd24gbWFjaGluZSBsZWFybmluZyBtb2RlbHMsIHJhbmRvbSBmb3Jlc3RzIGFuZCBMQVNTTywgd2hpY2ggYXJlIGJvdGggZnJlcXVlbnRseSB1c2VkIHRvIG1lYXN1cmUgdGhlIHJlbGF0aXZlIGltcG9ydGFuY2Ugb2YgdGhlIHByZWRpY3RvcnMgaW5jbHVkZWQgaW4gdGhlIG1vZGVscy4gIExhc3RseSwgd2UnbGwgZ2VuZXJhdGUgdHJhaW5lZCBhbmQgdHVuZWQgbW9kZWxzIHVzaW5nIHRoaXMgcmVkdWNlZCBmZWF0dXJlIHNldCB3aGljaCBjYW4gYmUgdXNlZCBieSBvdGhlcnMgd2lzaCB0byBwcmVkaWN0IHRoZSBpZGVudGlmaWVkIG91dGNvbWVzLgoKKipTdWJqZWN0IGluY2x1c2lvbi4qKiBGb3IgdGhpcyBpbnZlc3RpZ2F0aW9uLCB3ZSB3aWxsIG9taXQgdGhlIGVudGlyZXR5IG9mIFdhdmUgMi4gIFRoaXMgaXMgY29tbW9ubHkgZG9uZSBpbiBhbmFseXNlcyBvZiBBZGRIZWFsdGggZGF0YSBkdWUgdGhlIGRlc2lnbiBvZiB0aGUgb3JpZ2luYWwgc3R1ZHkuICBPdGhlcndpc2UsIG91ciBkYXRhc2V0IHdpbGwgaW5jbHVkZSBvbmx5IHN1YmplY3RzIHdobyBoYXZlIHByZWRpY3RvciBhbmQgb3V0Y29tZSBkYXRhIGluIF9hbGxfIG9mIHRoZSB3YXZlcy4KCioqT3V0Y29tZSB2YXJpYWJsZXMuKiogSW4gdGhpcyBleHBlcmltZW50LCB3ZSBhc3Nlc3MgX3N1aWNpZGFsIGlkZWF0aW9uXyBhdCBXYXZlIDUuICAKCioqUHJlZGljdG9yIHZhcmlhYmxlcy4qKiBUaGUgcHJlZGljdG9ycyBmb3IgdGhlc2UgbW9kZWxzIGFyZSBoYW5kLXBpY2tlZCwgYW5kIGJhc2VkIG9uIHByZXZpb3VzIHdvcmssIHJlbGV2YW5jZSwgYW5kIHN1YmplY3QgbWF0dGVyIGV4cGVydGlzZS4gVGhlIHNldCBvZiBwcmVkaWN0b3JzIGFuZCB0aGUgc2V0IG9mIG91dGNvbWVzIGFyZSBkaXNqb2ludC4gIFByZWRpY3RvcnMgZnJvbSBXYXZlcyAxLTQgKGV4Y2x1ZGluZyBXYXZlIDIsIHNlZSBhYm92ZSkgYXJlIGluY2x1ZGVkLCBhbmQgd2lsbCBiZSBkZXRhaWxlZCBpbiB0aGUgZm9sbG93aW5nIGFuYWx5c2lzLgoKYGBge3IgbG9hZCBsaWJyYXJpZXMsIGluY2x1ZGU9RkFMU0V9CiMgVXNlIHBhY21hbiwgd2hpY2ggZm9yY2VzIGFuIGluc3RhbGwgaWYgdGhlIGxpYnJhcnkgaXNuJ3QgcHJlc2VudCBvbiB0aGUgcnVubmluZyBtYWNoaW5lCmlmICghcmVxdWlyZSgicGFjbWFuIikpIGluc3RhbGwucGFja2FnZXMoInBhY21hbiIpCiNwYWNtYW46OnBfaW5zdGFsbChwbG90bHkpCnBhY21hbjo6cF9sb2FkKHRpZHl2ZXJzZSwgaDJvLCBmdXJycikKCmBgYAoKYGBge3IgaW5pdGlhbGl6YXRpb25zLCBpbmNsdWRlPUZBTFNFfQpoMm8uaW5pdCgpIApoMm8ubm9fcHJvZ3Jlc3MoKQpmdXR1cmU6OnBsYW4obXVsdGlwcm9jZXNzKQpgYGAKCmBgYHtyIHNlZWRzIGZvciByZXByb2R1Y2liaWxpdHl9CnNlZWQ9IDM4OTUKc2V0LnNlZWQoc2VlZCkKYGBgCiMgUGlwZWxpbmUgb3ZlcnZpZXcKYGBge3IgZWNobz1GQUxTRSwgb3V0LndpZHRoPSc1MCUnfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygnLi9pbWcvcGlwZWxpbmVfb3ZlcnZpZXcuanBnJykKYGBgCiMgRGF0YXNldCBnZW5lcmF0aW9uCgpUaGUgcHJlZGljdG9ycyB3ZSB3aWxsIGJlIHVzaW5nIHdpbGwgYmUgdGhlIHRoZSB2YXJpYWJsZSBgcHJlZGljdG9yX2xpc3RgIGxvYWRlZCBmcm9tIGAxMC1pbXBvcnQtZGF0YS5SbWRgIGZpbGUuIFRoZXNlIGluaXRpYWwgc2V0IG9mIHByZWRpY3RvcnMgd2lsbCBiZSBiYXNlZCBvZiB0aGUgbGlzdCBvZiB2YXJpYWJsZXMgdGhhdCBkZXNjcmliZSBhbnhpZXR5LCBkZXByZXNzaW9uLCBhbmQgb3B0aW1pc20uCgpgYGB7ciBsb2FkIHJhdyBkYXRhfQojIyBzZXQgb3V0Y29tZSB2YXJpYWJsZSBvZiBpbnRlcmVzdApvdXRjb21lID0gJ2g1bW44JwoKd2F2ZV9kYXRhIDwtIGxvYWRfd2F2ZXMoMTo1KQoKZnVsbF9kYXRhc2V0IDwtIGdldF93b3JraW5nX2RhdGFzZXRfZnVsbCh3YXZlX2RhdGEsIGpvaW5fdHlwZSA9ICdmdWxsJykgCgpgYGAKCgpgYGB7ciBjb21wb3NlIHdvcmtpbmcgZGF0YXNldCwgd2FybmluZz1GYWxzZSwgbWVzc2FnZT1GYWxzZX0KIyMgT25seSBzdHVkeSB0aGUgc3ViamVjdHMgdGhhdCB3ZSdyZSBpbnRlcmVzdGVkIGluLgppbm5lcl9haWRzIDwtIGdldF9pbm5lcihsaXN0KHdhdmVfZGF0YVtbMV1dLCB3YXZlX2RhdGFbWzNdXSwgd2F2ZV9kYXRhW1s0XV0sIHdhdmVfZGF0YVtbNV1dKSkKCiMjIGdldCBuYV9sZXZlbHMgOiBkYXRhc2V0IHRvIHJlY29kZSBhbGwgc2tpcCBsZXZlbHMgaW4gdmFyaWFibGVzCm5hX2xldmVscyA8LSByZWFkX2NzdigibmFfbGV2ZWxzLmNzdiIpCgojIyB1c2UgdGhlIGZlYXR1cmVzIGFuZCBpZHMgdGhhdCB5b3Ugd2FudCB0byBzZWxlY3Qgb3V0IHdoYXQgeW91IHdhbnQKc3VpY2lkZV9kcyA8LSBmdWxsX2RhdGFzZXQgJT4lCiAgZmlsdGVyKGFpZCAlaW4lIGlubmVyX2FpZHMpICU+JQogIHJlbW92ZV9zdWJqZWN0c19ub3RfaW5fd2F2ZTEoKSAlPiUKICBhZGRfZGVtb2dyYXBoaWNzKCkgJT4lCiAgYWRkX2Jpb19kZXNwYWlyKCkgJT4lIAogIGRwbHlyOjpzZWxlY3QoYWlkLCBvdXRjb21lLCBhbGxfb2YoYyhwcmVkaWN0b3JfbGlzdCwgZGVtb2dyYXBoaWNfYWdlX2xpc3QsIGRlbW9ncmFwaGljX2xpc3QpKSkgJT4lCiAgZHBseXI6OnNlbGVjdCgtYyhoNXdhaXN0LGg1Ym1pLGg1ZGJwLGg1YnBqY2xzLGg1YnBjbHM0LGg1c2JwKSkgJT4lCiAgcmVjb2RlX21pc3NpbmdfbGV2ZWxzKG5hX2xldmVscykKCmBgYAoKIyMgT3V0Y29tZSB2YXJpYWJsZTogQmluYXJpemluZyBhbmQgcmVjb2RpbmcgZGV0YWlscwoKVGhlIGZvbGxvd2luZyB0YWJsZSBkZXRhaWxzIHRoZSB2YWx1ZXMgcHJlc2VudCBpbiB0aGUgb3V0Y29tZSB2YXJpYWJsZS4gIEFzIHdlIGNhbiBzZWUsIHdlIG5lZWQgdG8gY29udmVydCB0aGUgdmFyaWFibGUgaW50byBhIGZhY3RvciwgYW5kIGRyb3AgdGhlIE5Bcy4KCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnN1aWNpZGVfZHMgJT4lIAogIGdyb3VwX2J5KGdldChvdXRjb21lKSkgJT4lIAogIHN1bW1hcmlzZSh0b3RhbCA9IG4oKSwgdHlwZSA9IGNsYXNzKGdldChvdXRjb21lKSkpCmBgYAoKVGhlIHRhYmxlIGFsc28gZGVtb25zdHJhdGVzIHRoYXQgdGhlIGNsYXNzZXMgYXJlIHZlcnkgaW1iYWxhbmNlZCwgd2l0aCBhYm91dCAxNHggdGhlIG5lZ2F0aXZlIGNsYXNzIGFzIGNvbXBhcmVkIHdpdGggdGhlIHBvc2l0aXZlIGNsYXNzLgoKYGBge3IgYmluYXJpemUgYW5kIGRyb3AgTkEgb3V0Y29tZSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0Kc3VpY2lkZV9kcyA8LSBzdWljaWRlX2RzICU+JQogIG11dGF0ZV9hdCh2YXJzKG91dGNvbWUpLCBhcy5mYWN0b3IpICU+JQogIGRyb3BfbmEob3V0Y29tZSkKYGBgCgpBZnRlciBkcm9wcGluZyB0aGUgTkFzLCB3ZSBzZWUgdGhhdCB3ZSBub3cgaGF2ZSA5MTY4IHJvd3MsIHdoaWNoIGlzIGNvbnNpc3RlbnQgd2l0aCB0aGUgdGFibGUgYWJvdmUuICBUaGUgZm9sbG93aW5nIHBsb3QgdmlzdWFsbHkgZGlzcGxheXMgdGhlIGNsYXNzIGRpc3RyaWJ1dGlvbi4KCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnN1aWNpZGVfZHMgJT4lIGV4cGxvcmVfb3V0Y29tZShvdXRjb21lKQpgYGAKVGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGF0YSBpcyBhIGRpc3RpbmN0IGltYmFsYW5jZSBhcyBub3RlZCBhYm92ZS4gIEkgdGhpbmsgdGhpcyB3YXJyYW50cyB1c2luZyBgcHJfYXVjYCBhcyB0aGUgb3B0aW1pemF0aW9uIGFuZCBzZWxlY3Rpb24gbWV0cmljLgoKIyBEYXRhIGV4cGxvcmF0aW9uIGFuZCB2aXN1YWxpemF0aW9uCkhlcmUsIHdlIGNvbW1lbnQgYWJvdXQgdGhlIGdlbmVyYWwgY2hhcmFjdGVyaXN0aWNzIG9mIHRoZSBkYXRhIGJhc2VkIG9uIHRoZSBwcm92aWRlZCB2aXN1YWxpemF0aW9ucy4gIFdlIGNvbW1lbnQgb24gbWlzc2luZ25lc3Mgb2YgZGF0YSwgYW55IHN0cmFuZ2Ugb3IgdW51c3VhbCBiZWhhdmlvciAoZS5nLiwgc3Ryb25nIGltYmFsYW5jZXMpLCBhbmQgYW55IGNvcnJlbGF0aW9uIHRoYXQgc3RpY2tzIG91dC4KCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiNSZXBvcnQgYWJvdXQgdGhlIGNoYXJhY3RlcmlzdGljcyBvZiB0aGUgc3ViamVjdHMgbGVmdCBvdXQgb2YgdGhlIGpvaW4Kc3VpY2lkZV9kcyAlPiUgZXhwbG9yZV9kcm9wcGVkKCkKCmBgYAoKYGBge3IgZWRhIGRpc3RyaWJ1dGlvbnMsIGZpZy53aWR0aCA9IDE1LCBmaWcuaGVpZ2h0PTE1fQojIFZpc3VhbGl6ZSBkaXN0cmlidXRpb25zIG9mIHZhcmlhYmxlcyBvZiBpbnRlcmVzdApzdWljaWRlX2RzICU+JSAKICBkcGx5cjo6c2VsZWN0KC1haWQpICU+JQogIGdyYXBoX2Jhcl9kaXNjcmV0ZShkZiA9IC4sCiAgICAgICAgICAgICAgICAgICAgIHBsb3RfdGl0bGUgPSAiRGlzdHJpYnV0aW9ucyBvZiBEaXNjcmV0ZSBWYXJpYWJsZXMiLAogICAgICAgICAgICAgICAgICAgICBtYXhfY2F0ZWdvcmllcyA9IDUwLAogICAgICAgICAgICAgICAgICAgICBudW1fcm93cyA9IDMsCiAgICAgICAgICAgICAgICAgICAgIG51bV9jb2xzID0gMywKICAgICAgICAgICAgICAgICAgICAgeF9heGlzX3NpemUgPSAxMiwKICAgICAgICAgICAgICAgICAgICAgeV9heGlzX3NpemUgPSAxMiwKICAgICAgICAgICAgICAgICAgICAgdGl0bGVfc2l6ZSA9IDE1KQpgYGAKCmBgYHtyIGVkYSBtaXNzaW5nfQpzdWljaWRlX2RzICU+JQogIGdyYXBoX21pc3Npbmcob25seV9taXNzaW5nID0gVFJVRSwKICAgICAgICAgICAgICAgIHRpdGxlID0gIlBlcmNlbnQgTWlzc2luZyIsCiAgICAgICAgICAgICAgICBib3hfbGluZV9zaXplID0gLjUsCiAgICAgICAgICAgICAgICBsYWJlbF9zaXplID0gLjUsCiAgICAgICAgICAgICAgICB4X2F4aXNfc2l6ZSA9IDEyLAogICAgICAgICAgICAgICAgeV9heGlzX3NpemUgPSAxMiwKICAgICAgICAgICAgICAgIHRpdGxlX3NpemUgPSAxNSkKCmBgYAoKYGBge3IgZWRhIGNvcnJlbGF0aW9uc30Kc3VpY2lkZV9kcyAlPiUKICAjZHBseXI6OnNlbGVjdCgxOjIwKSAlPiUKICBwYWlyd2lzZV9jcmFtZXJzX3YoKSAlPiUKICBwbG90X2NyYW1lcl92KHhfYXhpc19hbmdsZSA9IDkwLAogICAgICAgICAgICAgICAgcGxvdF90aXRsZSA9ICJBc3NvY2lhdGlvbiBhbW9uZyBDYXRlZ29yaWNhbCBWYXJpYWJsZXMiLAogICAgICAgICAgICAgICAgaW50ZXJhY3RpdmUgPSBUUlVFKQpgYGAKVGhlIGNvcnJlbGF0aW9uIHBsb3Qgc3VnZ2VzdHMgdGhhdCB0aGVyZSBhcmUgc2V2ZXJhbCB2YXJpYWJsZXMgd2UgbWlnaHQgdGhpbmsgYWJvdXQgcmVtb3ZpbmcuICBGaXJzdGx5LCB0aGVyZSBhcmUgbW9kZXJhdGUgY29ycmVsYXRpb25zIGFtb25nIHRoZSBpbmRpdmlkdWFsIHByZWRpY3RvciBibG9ja3M7IHRoaXMgaXMgZHVlIHRvIHRoZSB3YXkgdGhhdCB0aGV5IGFyZSBvcmRlcmVkLCBzaW5jZSB0aGV5J3JlIGVzc2VudGlhbGx5IGdyb3VwZWQgaW50byBzdWJzZXRzLiAgQWRkdGlvbmFsbHksIHNvbWUgcHJlZGljdG9yIHBhaXJzIGhhdmUgZXh0cmVtZWx5IGhpZ2ggY29ycmVsYXRpb25zLCBsaWtlIChoNGlkNWosIGg0bWgyNikgKGNvcnJlbGF0aW9uIG9mIDAuNzIpLCBhbmQgbWFueSB2YXJpYWJsZXMgaW4gdGhlIGg0bWgqIHNlcmllcy4gIChoM2lkNWosIGg0aWQ1aCkgYWxzbyBoYXMgaGlnaCBjb3JyZWxhdGlvbiA+IDAuNS4gIFdlIG1heSB3YW50IHRvIGNvbnNpZGVyIHJlbW92aW5nIHNldmVyYWwgb2YgdGhlc2UgaW4gYWRkaXRpb24gdG8gdGhlIGFnZSB2YXJpYWJsZXMgYmVjYXVzZSBpdCBtYXkgY2F1c2UgZmVhdHVyZSBpbXBvcnRhbmNlIG1hc2tpbmcgd2l0aGluIG91ciBhcHByb2FjaGVzLgoKIyBNYWNoaW5lIGxlYXJuaW5nIHNwbGl0IG9mIHRoZSBkYXRhCgpJbiB0aGlzIHNlY3Rpb24sIHdlIHNwbGl0IHRoZSBkYXRhIHRvIGVuc3VyZSB0aGF0IG91ciBtb2RlbCBpcyBhYmxlIHRvIGdlbmVyYWxpemUgdG8gb3RoZXIgZGF0YXNldHMuCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIyBzcGxpdCB0aGUgZGF0YSBpbnRvIHJlbGV2YW50IHByb3BvcnRpb25zIGRlc2lyZWQKZGF0YV9zcGxpdHMgPC0gc3VpY2lkZV9kcyAlPiUKICBzcGxpdF9kYXRhKHN0cmF0X3ZhciA9IG91dGNvbWUsIHJhdGlvcz1jKDAuNywgMC4yLCAwLjEpKQoKIyBhc3NlbWJsZSBsaXN0CnRyYWluaW5nX2RmIDwtIGRhdGFfc3BsaXRzJHRyYWluCnZhbGlkYXRpb25fZGYgPC0gZGF0YV9zcGxpdHMkdmFsaWQKdGVzdGluZ19kZiA8LSBkYXRhX3NwbGl0cyR0ZXN0CmBgYAoKIyBSb2J1c3QgZmVhdHVyZSBldmFsdWF0aW9uIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQoKIyMgUkYgbW9kZWwKVGhlIFJGIG1vZGVscyBhcmUgY2hvc2VuIGJhc2VkIG9uIGEgZ3JpZCBzZWFyY2ggdXNpbmcgdGhlIGZvbGxvd2luZyB0aGUgcGFyYW1ldGVyczogCgogIC0gbWF4IGRlcHRoOiBtYXhpbXVtIGRlcHRoIGFsbG93ZWQgZm9yIGEgc2luZ2xlIHRyZWUgaW4gdGhlIFJGICAKICAtIG51bWJlciBvZiB0cmVlczogbWF4aW11bSBudW1iZXIgb2YgdHJlZXMgYWxsb3dlZCBpbiB0aGUgUkYgIAogIC0gbXRyaWVzOiB0aGUgbnVtYmVyIG9mIGNvbHVtbnMgc2FtcGxlZCBmb3IgZWFjaCB0cmVlIHNwbGl0ICAKICAtIG1pbl9yb3dzOiB0aGUgbWluaW11bSBudW1iZXIgb2Ygcm93cyByZXF1aXJlZCB0byBzcGxpdCB0aGUgaW50ZXJuYWwgbm9kZQogIC0gYmFsYW5jZSBjbGFzc2VzOiB3aGV0aGVyIHRvIGJhbGFuY2UgdGhlIGNsYXNzZXMgb3Igbm90CiAgLSBzdG9wcGluZ19tZXRyaWM6IG1ldHJpYyB3aGljaCByZXN1bHRzIGluIGVhcmx5IHN0b3BwaW5nIG9mIHRyYWluaW5nIG9mIHRoZSBtb2RlbAogIC0gY2F0ZWdvcmljYWwgZW5jb2Rpbmc6IHVzZSBvbmUgaG90IGVuY29kaW5nIHRvIGNyZWF0ZSBzdHJhaWdodGZvcndhcmQgY29tcGFyaXNvbiB3aXRoIExBU1NPCgpgYGB7ciBmZWF0dXJlIHNlbGVjdGlvbiByZiwgaW5jbHVkZT1GQUxTRX0KCiMgU3BhbnMgb2YgaHlwZXIgcGFyYW1ldGVycyBmb3IgcmFuZG9tIGZvcmVzdApyZl9wYXJhbXMgPC0gbGlzdChtYXhfZGVwdGggPSA1MCwKICAgICAgICAgICAgICAgICAgbnRyZWVzID0gMTUwLAogICAgICAgICAgICAgICAgICBtdHJpZXMgPSBjKC0xLCAyMCksCiAgICAgICAgICAgICAgICAgIG1pbl9yb3dzID0gYyg1LCAxMCksCiAgICAgICAgICAgICAgICAgIHN0b3BwaW5nX3JvdW5kcyA9IDQsCiAgICAgICAgICAgICAgICAgIGJhbGFuY2VfY2xhc3NlcyA9IGMoVFJVRSwgRkFMU0UpLAogICAgICAgICAgICAgICAgICBzdG9wcGluZ19tZXRyaWMgPSAnQVVDUFInLAogICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbF9lbmNvZGluZyA9ICdvbmVfaG90X2V4cGxpY2l0JykKCiMgcmZfcGFyYW1zIDwtIGxpc3QobWF4X2RlcHRoID0gYygyMCwgNTApLAojICAgICAgICAgICAgICAgICAgIGJhbGFuY2VfY2xhc3NlcyA9IFRSVUUsCiMgICAgICAgICAgICAgICAgICAgY2F0ZWdvcmljYWxfZW5jb2Rpbmc9ICdvbmVfaG90X2V4cGxpY2l0JykKCiMgZGVmaW5lIG51bWJlciBvZiBib290c3RyYXBzCm5fYm9vdCA9IDUwCgpzdWljaWRlX3JmIDwtIG1vZGVsX2ZlYXR1cmVfc2VsZWN0aW9uKCJSRiIsdHJhaW5pbmdfZnJhbWUgPSB0cmFpbmluZ19kZiwKICAgICAgICAgICAgICAgICAgICAgIHZhbGlkYXRpb25fZnJhbWUgPSB2YWxpZGF0aW9uX2RmLAogICAgICAgICAgICAgICAgICAgICAgaHlwZXJfcGFyYW1zID0gcmZfcGFyYW1zLAogICAgICAgICAgICAgICAgICAgICAgb3V0Y29tZSA9IG91dGNvbWUsIG4gPSBuX2Jvb3QsIHNlZWQ9c2VlZCkKCmBgYAoKVGhlIGZvbGxvd2luZyB0YWJsZSBkaXNwbGF5cyB0aGUgbWVhbiBwZXJmb3JtYW5jZSBtZXRyaWNzIGZvciB0aGUgYm9vdHN0cmFwcGVkIG1vZGVscyBvbiB0aGUgdmFsaWRhdGlvbiBzZXQsIHJlbW92aW5nIHZhbHVlcyBmb3Igd2hpY2ggdGhlcmUgYXJlIE5BLgoKYGBge3IgZXZhbHVhdGUgYm9vdHN0cmFwIG1vZGVsIHBlcmZvcm1hbmNlIHJmLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQptZWFuX2JzX3JmX3BlcmYgPC0gZ2V0X21ldHJpY19zZXRfZnJvbV9wZXJmcyhzdWljaWRlX3JmJHBlcmZzKSAlPiUKICBkcGx5cjo6c2VsZWN0KGFjY3VyYWN5LCBtcGNlLCBzZW5zLCBzcGVjLCBwcHYsIG5wdiwgcm9jX2F1YywgcHJfYXVjLAogICAgICAgICB0bnMsIHRwcywgZm5zLCBmcHMsIG5vX24sIG5vX3AsICBlcnJfcmF0ZSwgYmFsX2FjY3VyYWN5LCBldmVyeXRoaW5nKCkpICU+JQogIHN1bW1hcmlzZV9pZihpcy5udW1lcmljLCBtZWFuLCBuYS5ybT1UUlVFKSAlPiUKICBtdXRhdGUobW9kZWwgPSAnYnNfcmYnKSAlPiUKICBkcGx5cjo6c2VsZWN0KG1vZGVsLCBldmVyeXRoaW5nKCkpCgptZWFuX2JzX3JmX3BlcmYKYGBgCkFzIHNob3duLCB0aGUgYm9vdHN0cmFwcGVkIG1vZGVscyB0ZW5kIHRvIGhhdmUgaGlnaCBzcGVjaWZpY2l0eSBidXQgbG93IHNlbnNpdGl2aXR5LCBpbmRpY2F0aW5nIHRoYXQgdGhlcmUgaXMgYSBjaGFsbGVuZ2UgaW4gaWRlbnRpZnlpbmcgc3ViamVjdHMgd2l0aCBzdWljaWRhbCBpZGVhdGlvbi4KCiMjIyBGZWF0dXJlIGltcG9ydGFuY2VzOiBSYW5kb20gRm9yZXN0CiMjIyMgTWVhbiBkZWNyZWFzZSBpbiBpbXB1cml0eSAoTURJKQpgYGB7cn0KYm9vdF9yZl9tZGkgPC0gc3VpY2lkZV9yZiRtZGkgJT4lCiAgZ2V0X21lZGlhbl9wbGFjZW1lbnQodXNlX2Jhc2VfdmFyID0gVFJVRSkgJT4lCiAgYWRkX2F0dHJpYnV0ZV9uYW1lcygncHJlZGljdG9yJywgZnVsbF9kYXRhc2V0KSAlPiUKICBkcGx5cjo6c2VsZWN0KHByZWRpY3RvciwgYXR0X25hbWUsIG92ZXJhbGxfcmFuaykKCmhlYWQoYm9vdF9yZl9tZGksIDIwKQpgYGAKVGhpcyB0YWJsZSByZXR1cm5zIHRoZSBNREkgdmFyaWFibGUgaW1wb3J0YW5jZSByYW5rcyB0aGF0IHJldHVybmVkIGZyb20gZWFjaCBvZiB0aGUgYm9vdHN0cmFwcGVkIG1vZGVscy4KCmBgYHtyIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMTJ9CiMgTmVlZHMgdG8gYmUgZml4ZWQgc28gdGhhdCBheGVzIGRvbid0IG92ZXJsYXAgZWFjaCBvdGhlciBhbmQgb2JzY3VyZSB1bmRlcnN0YW5kaW5nCnBsb3RfcGxhY2VtZW50X2JveHBsb3Qoc3VpY2lkZV9yZiRtZGkpCmBgYAoKIyMjIyBQZXJtdXRhdGlvbiBpbXBvcnRhbmNlCk5vdywgbGV0J3MgbG9vayBhdCB0aGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZToKCmBgYHtyIGdldCByZiBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpib290X3JmX3Blcm1fcGx0IDwtIHN1aWNpZGVfcmYkbW9kZWxzICU+JQogIGdldF9hZ2dyZWdhdGVkX3Blcm11dGVfaW1wKHRyYWluaW5nX2RmLCBvdXRjb21lPW91dGNvbWUpCmBgYAoKYGBge3IgYWdncmVnYXRlIHJmIHBlcm0gcmVzdWx0c30KbWV0IDwtICdwcl9hdWMnCmJvb3RfcmZfcGVybSA8LSBib290X3JmX3Blcm1fcGx0ICU+JQogIGdldF9wZXJtdXRlX3BsYWNlbWVudChtZXRyaWNfb2k9bWV0KSAlPiUKICBhZGRfYXR0cmlidXRlX25hbWVzKCdwcmVkaWN0b3InLCBmdWxsX2RhdGFzZXQpICU+JQogIGRwbHlyOjpzZWxlY3QocHJlZGljdG9yLCBldmVyeXRoaW5nKCkpCgpoZWFkKGJvb3RfcmZfcGVybSwgMjApCmBgYAoKIyMjIyBNREkgdnMgUGVybXV0YXRpb24gaW1wb3J0YW5jZQpJbiB0aGlzIHN0ZXAsIHdlIGFzc2VzcyB0aGUgZGlmZmVyZW5jZXMgZ2VuZXJhdGVkIGJldHdlZW4gdGhlIGRpZmZlcmVudCB0eXBlcyBvZiBpbXBvcnRhbmNlcy4KYGBge3IgZmlnLndpZHRoID0gMTYsIGZpZy5oZWlnaHQgPSAxNH0KY2JpbmQoYm9vdF9yZl9tZGlbMToyMCxdLCBkcGx5cjo6c2VsZWN0KGJvb3RfcmZfcGVybVsxOjIwLF0sIC1hbGxfb2YobWV0KSkpCmBgYApBcyBzaG93biwgdGhlIE1ESSBpbXBvcnRhbmNlIHN1ZmZlcnMgZnJvbSBpbWJhbGFuY2VzIGR1ZSB0byB0aGUgbnVtYmVyIG9mIHZhbHVlcyBhc3NvY2lhdGVkIHdpdGggYSBwcmVkaWN0b3IuICBCZWNhdXNlIHRoZSB3YXZlIGFnZXMgaGF2ZSBzbyBtYW55IG1vcmUgdmFsdWVzIHRoYW4gdGhlIG90aGVyIGZhY3RvcnMsIHRoaXMgYXJ0aWZpY2lhbGx5IGluZmxhdGVzIHRoZWlyIGltcG9ydGFuY2UgaW4gTURJLiAgVGhlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UgaXMgbW9yZSBpbnR1aXRpdmUuCgpgYGB7ciBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEyfQpwbG90X3Blcm11dGVfdmFyX2ltcChib290X3JmX3Blcm0sIG1ldHJpYyA9IHByX2F1YykKYGBgCgoKIyMgTEFTU08gbW9kZWwKSW4gdGhpcyBzdGVwLCB3ZSBtb2RlbCB0aGUgcmVsYXRpb24gYmV0d2VlbiB0aGUgb3V0Y29tZXMgYW5kIHRoZSBwcmVkaWN0b3JzIHVzaW5nIGEgbGluZWFyIHJlZ3Jlc3Npb24gd2l0aCBMMiByZWd1bGFyaXphdGlvbi4gIFRoaXMgZHJpdmVzIHRoZSBpbXBvcnRhbmNlIG9mIHVuaW1wb3J0YW50IGFuZCByZWR1ZGFudCBmZWF0dXJlcyB0b3dhcmRzIHplcm8uCgpgYGB7ciBmZWF0dXJlIHNlbGVjdGlvbiBsYXNzbywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBGdW5jdGlvbiBwYXJhbWV0ZXJzCmxhc3NvX3BhcmFtcyA8LSBsaXN0KGFscGhhID0gYygxKSkKIyBDYWxsIG1vZGVsaW5nIGZ1bmN0aW9uIHVzaW5nIGZ1bmN0aW9uIHBhcmFtZXRlcnMgYW5kIHNob3cgdmlzdWFsaXphdGlvbiBvZiByZXN1bHRzLiAgUmVjb21tZW5kIHRoZSBudW1iZXIgb2YgZmVhdHVyZXMgdGhhdCBzaG91bGQgYmUgdXNlZC4gIFJlcG9ydCBwZXJmb3JtYW5jZSBtZXRyaWMgc3RhdHMuCgpzdWljaWRlX2xhc3NvIDwtIG1vZGVsX2ZlYXR1cmVfc2VsZWN0aW9uKCAiTGFzc28iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpbmluZ19mcmFtZSA9IHRyYWluaW5nX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWxpZGF0aW9uX2ZyYW1lID0gdmFsaWRhdGlvbl9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaHlwZXJfcGFyYW1zID0gbGFzc29fcGFyYW1zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvdXRjb21lID0gb3V0Y29tZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4gPSBuX2Jvb3QpCmBgYAoKYGBge3IgZXZhbHVhdGUgYm9vdHN0cmFwIG1vZGVsIHBlcmZvcm1hbmNlIGxhc3NvLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQptZWFuX2JzX2xhc3NvX3BlcmYgPC0gZ2V0X21ldHJpY19zZXRfZnJvbV9wZXJmcyhzdWljaWRlX2xhc3NvJHBlcmZzKSAlPiUKICBkcGx5cjo6c2VsZWN0KGFjY3VyYWN5LCBtcGNlLCBzZW5zLCBzcGVjLCBwcHYsIG5wdiwgcm9jX2F1YywgcHJfYXVjLAogICAgICAgICB0bnMsIHRwcywgZm5zLCBmcHMsIG5vX24sIG5vX3AsICBlcnJfcmF0ZSwgYmFsX2FjY3VyYWN5LCBldmVyeXRoaW5nKCkpICU+JQogIHN1bW1hcmlzZV9pZihpcy5udW1lcmljLCBtZWFuLCBuYS5ybT1UUlVFKSAlPiUgCiAgbXV0YXRlKG1vZGVsPSdic19sYXNzbycpICU+JQogIGRwbHlyOjpzZWxlY3QobW9kZWwsIGV2ZXJ5dGhpbmcoKSkKCm1lYW5fYnNfbGFzc29fcGVyZgpgYGAKCiMjIyBGZWF0dXJlIGltcG9ydGFuY2VzOiBMQVNTTwojIyMjIENvZWZmaWNpZW50LWJhc2VkIHZhcmlhYmxlIGltcG9ydGFuY2UKYGBge3J9CmJvb3RfbGFzc29fbWRpIDwtIHN1aWNpZGVfbGFzc28kbWRpICU+JQogIGdldF9tZWRpYW5fcGxhY2VtZW50KHVzZV9iYXNlX3ZhciA9IFRSVUUpICU+JQogIGFkZF9hdHRyaWJ1dGVfbmFtZXMoJ3ByZWRpY3RvcicsIGZ1bGxfZGF0YXNldCkgJT4lCiAgZHBseXI6OnNlbGVjdChwcmVkaWN0b3IsIGF0dF9uYW1lLCBvdmVyYWxsX3JhbmspCgpoZWFkKGJvb3RfbGFzc29fbWRpLCAyMCkKYGBgCgpgYGB7ciBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEyfQpwbG90X3BsYWNlbWVudF9ib3hwbG90KHN1aWNpZGVfbGFzc28kbWRpKQpgYGAKCiMjIyMgUGVybXV0YXRpb24gaW1wb3J0YW5jZQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpib290X2xhc3NvX3Blcm1fcGx0IDwtIHN1aWNpZGVfbGFzc28kbW9kZWxzICU+JQogIGdldF9hZ2dyZWdhdGVkX3Blcm11dGVfaW1wKHRyYWluaW5nX2RmLCBvdXRjb21lPW91dGNvbWUpCmBgYAoKYGBge3IgYWdncmVnYXRlIGxhc3NvIHBlcm11dGF0aW9ucyBhbmQgZ2V0IG1ldHJpY3N9CmJvb3RfbGFzc29fcGVybSA8LSBib290X2xhc3NvX3Blcm1fcGx0ICU+JQogIGdldF9wZXJtdXRlX3BsYWNlbWVudChtZXRyaWNfb2k9bWV0KSAlPiUgI3NldCBpbiByYW5kb20gZm9yZXN0IHNlY3Rpb24KICBhZGRfYXR0cmlidXRlX25hbWVzKCdwcmVkaWN0b3InLCBmdWxsX2RhdGFzZXQpICU+JQogIGRwbHlyOjpzZWxlY3QocHJlZGljdG9yLCBldmVyeXRoaW5nKCkpCgpoZWFkKGJvb3RfbGFzc29fcGVybSwgMjApCmBgYAoKYGBge3IgcGxvdCBsYXNzbyBwZXJtdXRhdGlvbiwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxMn0KcGxvdF9wZXJtdXRlX3Zhcl9pbXAoYm9vdF9sYXNzb19wZXJtLCBtZXRyaWMgPSBwcl9hdWMpCmBgYAoKIyMjIyBDb2VmZmljaWVudCB2cy4gUGVybXV0YXRpb24gaW1wb3J0YW5jZQpOb3csIHdlIGNvbXBhcmUgdGhlIGZlYXR1cmUgaW1wb3J0YW5jZXMgZ2VuZXJhdGVkIGJ5IHRoZSB0d28gZGlmZmVyZW50IGFwcHJvYWNoZXMuICBUaGUgdHJhZGl0aW9uYWwgbWV0aG9kIG9mIGV2YWx1YXRpbmcgZmVhdHVyZSBpbXBvcnRhbmNlIGZvciByZWdyZXNzaW9uIG1ldGhvZHMgaXMgdGhyb3VnaCBhbmFseXNpcyBvZiB0aGUgY29lZmZpY2llbnRzLgpgYGB7ciBmaWcud2lkdGggPSAxNiwgZmlnLmhlaWdodCA9IDE0fQpjYmluZChib290X2xhc3NvX21kaVsxOjIwLF0sIGRwbHlyOjpzZWxlY3QoYm9vdF9sYXNzb19wZXJtWzE6MjAsXSwgLW1ldCkpCmBgYAoKIyMgQ29tcGFyaXNvbjogTW9kZWwgVHlwZSBNZWFuIFBlcmZvcm1hbmNlClRoZSBmb2xsb3dpbmcgdGFibGUgY29tcGFyZXMgdGhlIG1lYW4gcGVyZm9ybWFuY2Ugb2YgYm9vdHN0cmFwcGVkIHJhbmRvbSBmb3Jlc3RzIHRvIHRoZSBtZWFuIHBlcmZvcm1hbmNlIG9mIGJvb3RzdHJhcHBlZCBMQVNTTyBtZXRob2RzLgpgYGB7cn0KYnNfY29tcF9wZXJmcyA8LSByYmluZChtZWFuX2JzX3JmX3BlcmYsIG1lYW5fYnNfbGFzc29fcGVyZikgCmJzX2NvbXBfcGVyZnMKYGBgCgojIyBDb21wYXJpc29uOiBNb2RlbCBUeXBlIEZlYXR1cmUgSW1wb3J0YW5jZQpIZXJlLCB3ZSBsb29rIGF0IHRoZSBhZ2dyZWdhdGVkIHJlc3VsdHMgb2YgdGhlIGJvb3RzdHJhcHBlZCBwcmVkaWN0b3JzIGFuZCBjb21wYXJlIHRoZSBtb2RlbHMgZ2VuZXJhdGVkIHRvIGVhY2ggb3RoZXIuCmBgYHtyfQpqb2luZWRfcmVzdWx0cyA8LSBib290X3JmX3Blcm0gJT4lCiAgZHBseXI6OnNlbGVjdCgtbWV0KSAlPiUKICBmdWxsX2pvaW4oZHBseXI6OnNlbGVjdChib290X2xhc3NvX3Blcm0sIC1tZXQpLCBieT1jKCJwcmVkaWN0b3IiLCAiYXR0X25hbWUiKSwgc3VmZml4PWMoJy5yZicsICcubGFzc28nKSkgJT4lCiAgbXV0YXRlKG1lYW5fcmFuayA9IChvdmVyYWxsX3JhbmsucmYrb3ZlcmFsbF9yYW5rLmxhc3NvKS8yKSAlPiUKICBhcnJhbmdlKG1lYW5fcmFuaykKCmhlYWQoam9pbmVkX3Jlc3VsdHMsIDIwKQpgYGAKClRoZSBmb2xsb3dpbmcgdmlzdWFsaXphdGlvbiBwcm92aWRlcyB0aGUgaW50dWl0aW9uIGFib3V0IHRoZSBkaWZmZXJlbmNlcyBpbiB0aGUgcmFua2luZ3MgYmV0d2VlbiBtb2RlbCB0eXBlcy4gIFRoZXkncmUgb3JkZXJlZCBieSB0aGUgb3ZlcmFsbCBtZWFuIGltcG9ydGFuY2UsIGFuZCBmb3IgYSBnaXZlbiB2YXJpYWJsZSwgdGhlIGRpZmZlcmVuY2VzIGluIHJhbmsgYXJlIHNob3duLgpgYGB7ciBmaWcud2lkdGggPSAxNiwgZmlnLmhlaWdodCA9IDE0fQojIENvbXBhcmlzb24gb2YgdG9wX24gZmVhdHVyZXMKam9pbmVkX3Jlc3VsdHMgJT4lCiAgY29tcGFyZV9mZWF0dXJlX3NlbGVjdChpbnRlcmFjdGl2ZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICB0b3BfbiA9IDEwMCwKICAgICAgICAgICAgICAgICAgICAgICAgIG9wYWNpdHkgPSAwLjUwLAogICAgICAgICAgICAgICAgICAgICAgICAgcGxvdF90aXRsZSA9ICJQZXJtdXRhdGlvbiBJbXBvcnRhbmNlIG9mIFByZWRpY3RvcnMgYnkgTW9kZWwiKQpgYGAKCgojIEdlbmVyYXRpb24gb2YgZmluYWwgbW9kZWwgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9CgojIyBSRiBtb2RlbApJbiB0aGlzIHN0ZXAsIHdlIGJ1aWxkIHRoZSBmaW5hbCBtb2RlbCBmb3IgdGhlIHJhbmRvbSBmb3Jlc3QuICBXZSB1c2Ugc2xpZ2h0bHkgbW9yZSB2YWx1ZXMgaW4gb3JkZXIgdG8gY29tZSB1cCB3aXRoIHRoZSBiZXN0IG1vZGVsLCBrZWVwaW5nIGluIG1pbmQgdGhlIG51bWJlciBvZiBjb21iaW5hdGlvbnMgdGhhdCBhcmUgcmVxdWlyZWQgdG8gcnVuIHRvIGV2YWx1YXRlIHRoZSBncmlkLgpgYGB7ciBmaW5hbCBtb2RlbCBldmFsdWF0aW9uIHJmfQoKIyAjIFNwYW5zIG9mIGh5cGVyIHBhcmFtZXRlcnMgZm9yIHJhbmRvbSBmb3Jlc3QKcmZfcGFyYW1zIDwtIGxpc3QobWF4X2RlcHRoID0gNTAsCiAgICAgICAgICAgICAgICAgIG50cmVlcyA9IDE1MCwKICAgICAgICAgICAgICAgICAgbXRyaWVzID0gc2VxKC0xLCAzMCwgYnk9NSksCiAgICAgICAgICAgICAgICAgIG1pbl9yb3dzID0gc2VxKDUsIDYwLCBieT01KSwKICAgICAgICAgICAgICAgICAgYmFsYW5jZV9jbGFzc2VzID0gYyhUUlVFLCBGQUxTRSksCiAgICAgICAgICAgICAgICAgIHN0b3BwaW5nX21ldHJpYyA9ICdBVUNQUicsCiAgICAgICAgICAgICAgICAgIGNhdGVnb3JpY2FsX2VuY29kaW5nID0gJ29uZV9ob3RfZXhwbGljaXQnKQoKIyByZl9wYXJhbXMgPC0gbGlzdChtYXhfZGVwdGggPSBzZXEoMjAsIDUwLCAyMCksCiMgICAgICAgICAgICAgICAgICAgYmFsYW5jZV9jbGFzc2VzID0gVFJVRSwKIyAgICAgICAgICAgICAgICAgICBjYXRlZ29yaWNhbF9lbmNvZGluZz0gJ29uZV9ob3RfZXhwbGljaXQnKQoKIyBGdW5jdGlvbiBwYXJhbWV0ZXJzCmZpbmFsX21vZGVsX3JmIDwtIHJmX21vZGVsKG91dGNvbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWluaW5nX2ZyYW1lID0gdHJhaW5pbmdfZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkYXRpb25fZnJhbWUgPSB2YWxpZGF0aW9uX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICBuZm9sZHMgPSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICBoeXBlcl9wYXJhbXMgPSByZl9wYXJhbXMsIG1vZGVsX3NlZWQ9c2VlZCkKCmBgYAoKIyMjIFBlcmZvcm1hbmNlClRoZSBmaW5hbCByYW5kb20gZm9yZXN0IHBlcmZvcm1hbmNlIG1ldHJpY3MgYXJlIHNob3duIGJlbG93OgpgYGB7cn0KIyBzaG93IG1vZGVsIGZpbmFsIHBlcmZvcm1hbmNlCnByaW50KGZpbmFsX21vZGVsX3JmW1syXV0pCmBgYAoKIyMjIEZlYXR1cmVzOiBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlCmBgYHtyIGdldCBwbG90dGFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZmluYWxfcmZfcGVybV9wbHQgPC0gYyhmaW5hbF9tb2RlbF9yZltbMV1dKSAlPiUKICBnZXRfYWdncmVnYXRlZF9wZXJtdXRlX2ltcCh0cmFpbmluZ19kZiwgb3V0Y29tZT1vdXRjb21lKQpgYGAKCmBgYHtyIGdldCBtZXRyaWNzIGZvciBmaW5hbCByZn0KZmluYWxfcmZfcGVybSA8LSBmaW5hbF9yZl9wZXJtX3BsdCAlPiUKICBnZXRfcGVybXV0ZV9wbGFjZW1lbnQobWV0cmljX29pPW1ldCkgJT4lCiAgYWRkX2F0dHJpYnV0ZV9uYW1lcygncHJlZGljdG9yJywgZnVsbF9kYXRhc2V0KSAlPiUKICBkcGx5cjo6c2VsZWN0KHByZWRpY3RvciwgZXZlcnl0aGluZygpKQoKaGVhZChmaW5hbF9yZl9wZXJtLCAyMCkKYGBgCgpgYGB7ciBwbG90IHJmIGZpbmFsIHBlcm11dGF0aW9uLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEyfQpwbG90X3Blcm11dGVfdmFyX2ltcChmaW5hbF9yZl9wZXJtLCBtZXRyaWMgPSBwcl9hdWMpCmBgYAojIyMgQ29tcGFyaXNvbiB3aXRoIGJvb3RzdHJhcCByZXN1bHRzClRoaXMgc2VjdGlvbiBpbnZlc3RpZ2F0ZXMgdGhlIGRpZmZlcmVuY2VzIGluIHRoZSBib290c3RyYXAgcmVzdWx0cyB2cyB0aGUgZmVhdHVyZXMgZ2VuZXJhdGVkIGZyb20gdGhlIHJhbmRvbSBmb3Jlc3QgZmluYWwgbW9kZWwuICBUaGUgZm9sbG93aW5nIHRhYmxlIHNob3dzIHRoZSBvdmVyYWxsIGRpZmZlcmVuY2VzIGluIHJhbmsuCgpgYGB7cn0KcmZfam9pbmVkX3Jlc3VsdHMgPC0gZmluYWxfcmZfcGVybSAlPiUKICBkcGx5cjo6c2VsZWN0KC1tZXQpICU+JQogIGZ1bGxfam9pbihkcGx5cjo6c2VsZWN0KGJvb3RfcmZfcGVybSwgLW1ldCksIGJ5PWMoInByZWRpY3RvciIsICJhdHRfbmFtZSIpLCBzdWZmaXg9YygnLmZpbmFsJywgJy5ib290c3RyYXAnKSkgJT4lCiAgbXV0YXRlKG1lYW5fcmFuayA9IChvdmVyYWxsX3JhbmsuZmluYWwgKyBvdmVyYWxsX3JhbmsuYm9vdHN0cmFwKS8yKSAlPiUKICBhcnJhbmdlKG1lYW5fcmFuaykKCmhlYWQocmZfam9pbmVkX3Jlc3VsdHMsIDIwKQpgYGAKClRoZSBmb2xsb3dpbmcgcGxvdCBwcm92aWRlcyB2aXN1YWxpemF0aW9ucyBmb3IgdGhlIGRpZmZlcmVuY2UgaW4gdGhlIGZpbmFsIG1vZGVsIHJhbmtpbmdzIHZzIHRoZSBib290c3RyYXAuCgpgYGB7ciBmaWcuaGVpZ2h0PTE0LCBmaWcud2lkdGg9MTZ9CiMgQ29tcGFyaXNvbiBvZiB0b3BfbiBmZWF0dXJlcwpyZl9qb2luZWRfcmVzdWx0cyAlPiUKICBjb21wYXJlX2ZlYXR1cmVfc2VsZWN0KHNlbF9jb2xzID0gYygib3ZlcmFsbF9yYW5rLmZpbmFsIiwgIm92ZXJhbGxfcmFuay5ib290c3RyYXAiKSwKICAgIGludGVyYWN0aXZlID0gVFJVRSwKICAgIHRvcF9uID0gMTAwLAogICAgb3BhY2l0eSA9IDAuNTAsCiAgICBwbG90X3RpdGxlID0gIlBlcm11dGF0aW9uIEltcG9ydGFuY2Ugb2YgUHJlZGljdG9yczogRmluYWwgdnMuIEJvb3RzdHJhcCIpCmBgYAoKIyMgTEFTU08gbW9kZWwKTm93LCB3ZSBjcmVhdGUgdGhlIGZpbmFsIG1vZGVsIGZvciBMQVNTTy4gIFRoZXJlIGlzIG5vIHN1YnN0YW50aWFsIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGlzIG1ldGhvZCBhbmQgdGhlIGJvb3RzdHJhcCBtZXRob2RzLCBvdGhlciB0aGFuIHRoZSBkYXRhIHVwb24gd2hpY2ggdGhlIG1vZGVsIGlzIGJlaW5nIGJ1aWx0LgpgYGB7ciBmaW5hbCBtb2RlbCBldmFsdWF0aW9uIGxhc3NvLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIEZ1bmN0aW9uIHBhcmFtZXRlcnMKbGFzc29fcGFyYW1zIDwtIGxpc3QoYWxwaGEgPSBjKDEpKQoKZmluYWxfbW9kZWxfbGFzc28gPC0gbGFzc29fbW9kZWwodHJhaW5pbmdfZnJhbWUgPSB0cmFpbmluZ19kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRhdGlvbl9mcmFtZSA9IHZhbGlkYXRpb25fZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG91dGNvbWUgPSBvdXRjb21lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZm9sZHMgPSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoeXBlcl9wYXJhbXMgPSBsYXNzb19wYXJhbXMpCmBgYAoKVGhlIGZpbmFsIExBU1NPIHBlcmZvcm1hbmNlIG1ldHJpY3MgYXJlIHNob3duIGJlbG93OgpgYGB7cn0KIyBzaG93IG1vZGVsIGZpbmFsIHBlcmZvcm1hbmNlCnByaW50KGZpbmFsX21vZGVsX2xhc3NvW1syXV0pCmBgYAoKIyMjIEZlYXR1cmVzOiBwZXJtdXRhdGlvbiBpbXBvcnRhbmNlCmBgYHtyIGdldCBwbG90dGFibGUgcGVybXV0YXRpb24gaW1wb3J0YW5jZSBsYXNzbywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZmluYWxfbGFzc29fcGVybV9wbHQgPC0gYyhmaW5hbF9tb2RlbF9sYXNzb1tbMV1dKSAlPiUKICBnZXRfYWdncmVnYXRlZF9wZXJtdXRlX2ltcCh0cmFpbmluZ19kZiwgb3V0Y29tZT1vdXRjb21lKQpgYGAKCmBgYHtyIGdldCBwZXJtdXRlIGZlYXR1cmVzIGZvciBmaW5hbCBsYXNzb30KZmluYWxfbGFzc29fcGVybSA8LSBmaW5hbF9sYXNzb19wZXJtX3BsdCAlPiUKICBnZXRfcGVybXV0ZV9wbGFjZW1lbnQobWV0cmljX29pPW1ldCkgJT4lCiAgYWRkX2F0dHJpYnV0ZV9uYW1lcygncHJlZGljdG9yJywgZnVsbF9kYXRhc2V0KSAlPiUKICBkcGx5cjo6c2VsZWN0KHByZWRpY3RvciwgZXZlcnl0aGluZygpKQoKaGVhZChmaW5hbF9sYXNzb19wZXJtLCAyMCkKYGBgCgpgYGB7ciBwbG90IGxhc3NvIGZpbmFsIHBlcm11dGF0aW9uLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEyfQpwbG90X3Blcm11dGVfdmFyX2ltcChmaW5hbF9sYXNzb19wZXJtLCBtZXRyaWMgPSBwcl9hdWMpCmBgYAojIyMgQ29tcGFyaXNvbiB3aXRoIGJvb3RzdHJhcCByZXN1bHRzClRoaXMgc2VjdGlvbiBpbnZlc3RpZ2F0ZXMgdGhlIGRpZmZlcmVuY2VzIGluIHRoZSBib290c3RyYXAgcmVzdWx0cyB2cyB0aGUgZmVhdHVyZXMgZ2VuZXJhdGVkIGZyb20gdGhlIExBU1NPIGZpbmFsIG1vZGVsLiAgVGhlIGZvbGxvd2luZyB0YWJsZSBzaG93cyB0aGUgb3ZlcmFsbCBkaWZmZXJlbmNlcyBpbiByYW5rLgoKYGBge3J9Cmxhc3NvX2pvaW5lZF9yZXN1bHRzIDwtIGZpbmFsX2xhc3NvX3Blcm0gJT4lCiAgZHBseXI6OnNlbGVjdCgtbWV0KSAlPiUKICBmdWxsX2pvaW4oZHBseXI6OnNlbGVjdChib290X2xhc3NvX3Blcm0sIC1tZXQpLCBieT1jKCJwcmVkaWN0b3IiLCAiYXR0X25hbWUiKSwgc3VmZml4PWMoJy5maW5hbCcsICcuYm9vdHN0cmFwJykpICU+JQogIG11dGF0ZShtZWFuX3JhbmsgPSAob3ZlcmFsbF9yYW5rLmZpbmFsICsgb3ZlcmFsbF9yYW5rLmJvb3RzdHJhcCkvMikgJT4lCiAgYXJyYW5nZShtZWFuX3JhbmspCgpoZWFkKGxhc3NvX2pvaW5lZF9yZXN1bHRzLCAyMCkKYGBgCgpUaGUgZm9sbG93aW5nIHBsb3QgcHJvdmlkZXMgdmlzdWFsaXphdGlvbnMgZm9yIHRoZSBkaWZmZXJlbmNlIGluIHRoZSBmaW5hbCBtb2RlbCByYW5raW5ncyB2cyB0aGUgYm9vdHN0cmFwLgoKYGBge3IgZmlnLndpZHRoID0gMTYsIGZpZy5oZWlnaHQgPSAxNH0KIyBDb21wYXJpc29uIG9mIHRvcF9uIGZlYXR1cmVzCmxhc3NvX2pvaW5lZF9yZXN1bHRzICU+JQogIGNvbXBhcmVfZmVhdHVyZV9zZWxlY3Qoc2VsX2NvbHMgPSBjKCJvdmVyYWxsX3JhbmsuZmluYWwiLCAib3ZlcmFsbF9yYW5rLmJvb3RzdHJhcCIpLAogICAgaW50ZXJhY3RpdmUgPSBUUlVFLAogICAgdG9wX24gPSAxMDAsCiAgICBvcGFjaXR5ID0gMC41MCwKICAgIHBsb3RfdGl0bGUgPSAiUGVybXV0YXRpb24gSW1wb3J0YW5jZSBvZiBQcmVkaWN0b3JzOiBGaW5hbCB2cy4gQm9vdHN0cmFwIikKYGBgCgojIyBDb21wYXJpc29uOiBGaW5hbCBtb2RlbCBmZWF0dXJlcwpIZXJlLCB3ZSBjb21wYXJlIHRoZSBmZWF0dXJlcyBnZW5lcmF0ZWQgYnkgdGhlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UgYmV0d2VlbiB0aGUgdHdvIGZpbmFsIG1vZGVscy4KCmBgYHtyfQpyZl9sYXNzb19maW5hbF9qb2luZWRfcmVzdWx0cyA8LSBmaW5hbF9yZl9wZXJtICU+JQogIGRwbHlyOjpzZWxlY3QoLW1ldCkgJT4lCiAgZnVsbF9qb2luKGRwbHlyOjpzZWxlY3QoZmluYWxfbGFzc29fcGVybSwgLW1ldCksIGJ5PWMoInByZWRpY3RvciIsICJhdHRfbmFtZSIpLCBzdWZmaXg9YygnLnJmJywgJy5sYXNzbycpKSAlPiUKICBtdXRhdGUobWVhbl9yYW5rID0gKG92ZXJhbGxfcmFuay5yZitvdmVyYWxsX3JhbmsubGFzc28pLzIpICU+JQogIGFycmFuZ2UobWVhbl9yYW5rKQoKaGVhZChyZl9sYXNzb19maW5hbF9qb2luZWRfcmVzdWx0cywgMjApCmBgYAoKVGhlIGZvbGxvd2luZyB2aXN1YWxpemF0aW9uIHByb3ZpZGVzIHRoZSBpbnR1aXRpb24gYWJvdXQgdGhlIGRpZmZlcmVuY2VzIGluIHRoZSByYW5raW5ncyBiZXR3ZWVuIHRoZSBmaW5hbCBtb2RlbCB0eXBlcy4gIFRoZXkncmUgb3JkZXJlZCBieSB0aGUgb3ZlcmFsbCBtZWFuIGltcG9ydGFuY2UsIGFuZCBmb3IgYSBnaXZlbiB2YXJpYWJsZSwgdGhlIGRpZmZlcmVuY2VzIGluIHJhbmsgYXJlIHNob3duLgoKYGBge3IgZmlnLndpZHRoID0gMTYsIGZpZy5oZWlnaHQgPSAxNH0KIyBDb21wYXJpc29uIG9mIHRvcF9uIGZlYXR1cmVzCnJmX2xhc3NvX2ZpbmFsX2pvaW5lZF9yZXN1bHRzICU+JQogIGNvbXBhcmVfZmVhdHVyZV9zZWxlY3Qoc2VsX2NvbHMgPSBjKCJvdmVyYWxsX3JhbmsucmYiLCAib3ZlcmFsbF9yYW5rLmxhc3NvIiksCiAgICBpbnRlcmFjdGl2ZSA9IFRSVUUsCiAgICB0b3BfbiA9IDEwMCwKICAgIG9wYWNpdHkgPSAwLjUwLAogICAgcGxvdF90aXRsZSA9ICJQZXJtdXRhdGlvbiBJbXBvcnRhbmNlIG9mIFByZWRpY3RvcnM6IFJhbmRvbSBGb3Jlc3QgdnMgTGFzc28iKQpgYGAKCiMjIENvbXBhcmlzb246IEZpbmFsIG1vZGVsIHBlcmZvcm1hbmNlCldpdGggdGhlIGZpbmFsIG1vZGVscyBnZW5lcmF0ZWQsIHdlJ3JlIG5vdyBhYmxlIHRvIGNvbXBhcmUgdGhlaXIgcGVyZm9ybWFuY2UgbWV0cmljcy4KYGBge3IgZmluYWwgbW9kZWwgY29tcGFyaXNvbiwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxMn0KIyBDb21wYXJpc29uIG9mIHBlcmZvcm1hbmNlIG1ldHJpY3MKdmFsaWRfcGVyZiA8LSBnZXRfbWV0cmljX3NldF9mcm9tX3BlcmZzKHBlcmZfbGlzdCA9IGxpc3QoZmluYWxfbW9kZWxfcmZbWzJdXSwgZmluYWxfbW9kZWxfbGFzc29bWzJdXSkpICU+JQogIG11dGF0ZShtb2RlbCA9IGMoJ3JmJywgJ2xhc3NvJykpCgp0ZXN0aW5nX3BlcmYgPC0gZ2V0X21ldHJpY19zZXRfZnJvbV9tb2RlbHModGVzdGluZ19kZiwgbGlzdChmaW5hbF9tb2RlbF9yZltbMV1dLCBmaW5hbF9tb2RlbF9sYXNzb1tbMV1dKSwgb3V0PW91dGNvbWUpICU+JQogIG11dGF0ZShtb2RlbCA9IGMoJ3JmJywgJ2xhc3NvJykpCmBgYAoKKipWYWxpZGF0aW9uIGFuZCBzZWxlY3Rpb24uKioKVGhlIGZvbGxvd2luZyB0YWJsZSBzaG93cyB0aGUgY29tcGFyaXNvbiBiZXR3ZWVuIG1vZGVscyBpbiB0ZXJtcyBvZiB0aGUgdmFsaWRhdGlvbiBzZXQuICBXZSBjYW4gc2VsZWN0IG91ciBmaW5hbCBtb2RlbCBiYXNlZCBvbiB0aGUgYmVzdCBwZXJmb3JtaW5nIG1vZGVsIGFjY29yZGluZyB0byB0aGUgbWV0cmljLgpgYGB7cn0KcHJpbnQodmFsaWRfcGVyZikKYGBgCgoqKlRlc3RpbmcgcGVyZm9ybWFuY2UuKioKVGhlIGZvbGxvd2luZyBzaG93cyB0aGUgcGVyZm9ybWFuY2Ugb2YgYm90aCB0aGUgbW9kZWxzIG9uIHRoZSB0ZXN0IHNldC4gIE5vdGUgdGhhdCBhbHRob3VnaCB3ZSBkb24ndCB1c2UgdGhpcyB0ZXN0IHNldCB0byBldmFsdWF0ZSB0aGUgZmluYWwgbW9kZWxzLCB3ZSBjYW4gc3RpbGwgc2VlIGhvdyBvdXIgc2VsZWN0ZWQgbWV0aG9kIHdvdWxkIGhhdmUgcGVyZm9ybWVkLgpgYGB7cn0KcHJpbnQodGVzdGluZ19wZXJmKQpgYGAKClRoZSBmb2xsb3dpbmcgcGxvdHMgc2hvdyBhIGNvbXBhcmlzb24gYmV0d2VlbiB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlIG1vZGVscyBvbiB0aGUgdmFsaWRhdGlvbiBhbmQgdGVzdCBzZXRzLiAgQWdhaW4sIHdlIGRvbid0IGNob29zZSB0aGUgbW9kZWwgYmFzZWQgb24gdGhlIHRlc3Qgc2V0LCBidXQgY3VyaW9zaXR5IGRpY3RhdGVzIHRoYXQgd2UgdmlldyB0aGlzIHBlcmZvcm1hbmNlLgoKYGBge3IgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA2fQojIFNob3cgcGxvdHMgc2lkZSBieSBzaWRlCm1ldHJpY3Nfb2ZfaW50ZXJlc3QgPSBjKCdtb2RlbCcsICdhY2N1cmFjeScsICdiYWxfYWNjdXJhY3knLCAnbXBjZScsICdzZW5zJywgJ3NwZWMnLCAncHB2JywgJ25wdicsICdwcl9hdWMnLCAncm9jX2F1YycpCnZhbGlkX3BsdCA8LSBwbG90X21ldHJpY19zZXQoZHBseXI6OnNlbGVjdCh2YWxpZF9wZXJmLCBhbGxfb2YobWV0cmljc19vZl9pbnRlcmVzdCkpLCBwbG90X3RpdGxlID0gIk1vZGVsIGNvbXBhcmlzb24gZm9yIHZhbGlkYXRpb24gc2V0IikKdGVzdF9wbHQgPC0gcGxvdF9tZXRyaWNfc2V0KGRwbHlyOjpzZWxlY3QodGVzdGluZ19wZXJmLCBhbGxfb2YobWV0cmljc19vZl9pbnRlcmVzdCkpLCBwbG90X3RpdGxlID0gIk1vZGVsIGNvbXBhcmlzb24gZm9yIHRlc3Rpbmcgc2V0IikKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZ3JpZEV4dHJhOjphcnJhbmdlR3JvYih2YWxpZF9wbHQsIHRlc3RfcGx0LCBuY29sPTIsIG5yb3c9MSkpCmBgYAoKIyBPdXRjb21lIHZhcmlhYmxlIGRpc2N1c3Npb24KSGVyZSwgdGhlIHN1YmplY3QgbWF0dGVyIGV4cGVydHMgd2lsbCBjb21tZW50IG9uIHRoZSB0aGUgZGlmZmVyZW5jZXMgaW4gdGhlIGZlYXR1cmVzIG9idGFpbmVkIGJldHdlZW4gdGhlIHN0dWRpZWQgb3V0Y29tZXMgdmFyaWFibGVzIGFuZCBkaXNjdXNzIHRoZSBkaXNjcmVwYW5jaWVzIGFuZC9vciBjb2hlc2lvbi4KCmBgYHtyIG91dGNvbWUgdmFyaWFibGUgY29tcGFyaXNvbn0KIyBTaG93IGRpZmZlcmVuY2VzIGluIGZlYXR1cmVzIG9idGFpbmVkCgpgYGAKCg==